package com.yingsheng.bi.hks.web.component;

import com.yingsheng.bi.hks.basic.utils.Utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.regex.Pattern;

@Slf4j
public class HttpLoggerFilter implements Filter {

    @Value("#{'${http.log.exclude.svcPath:}'.split(',')}")
    private String[] excludePaths;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if (log.isInfoEnabled() && isServletHttpRequest(servletRequest, servletResponse)) {
            HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
            if (!exclude(httpRequest)) {
                if (isGetMethod(httpRequest)) {
                    String queryString = httpRequest.getQueryString();
                    logInfo(httpRequest.getRequestURI(), httpRequest.getMethod(), "[request]", queryString);
                    filterChain.doFilter(servletRequest, servletResponse);
                } else if (isPostMethod(httpRequest)) {
                    HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
                    HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest);
                    ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(httpResponse);
                    String httpReqBodyString = requestWrapper.getReqBodyString();
                    logInfo(httpRequest.getRequestURI(), httpRequest.getMethod(), "[request]", httpReqBodyString);
                    filterChain.doFilter(requestWrapper, responseWrapper);
                    if (responseWrapper.getStatus() == 200) {
                        String httpResBodyString = getResBodyString(responseWrapper);
                        logInfo(httpRequest.getRequestURI(), httpRequest.getMethod(), "[response]", httpResBodyString);
                    }
                } else {
                    filterChain.doFilter(servletRequest, servletResponse);
                }
            } else {
                filterChain.doFilter(servletRequest, servletResponse);
            }
        } else {
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }

    private boolean exclude(HttpServletRequest httpRequest) {
        String contentType = httpRequest.getContentType();
        if (contentType != null && contentType.contains("multipart/form-data")) {
            return true;
        }
        String servicePath = httpRequest.getRequestURI();
        if (servicePath.contains("/actuator/") || servicePath.contains("/swagger")) {
            return true;
        }
        if (Utils.isEmpty(this.excludePaths)) {
            return false;
        }
        for (String excludePath : this.excludePaths) {
            if (Utils.isBlank(excludePath)) {
                continue;
            } else if (excludePath.equals(servicePath)) {
                return true;
            } else if (servicePath.startsWith(excludePath)) {
                return true;
            } else {
                Pattern pattern = Pattern.compile(excludePath);
                return pattern.matcher(servicePath).matches();
            }
        }
        return false;
    }

    private boolean isServletHttpRequest(ServletRequest servletRequest, ServletResponse servletResponse) {
        return servletRequest instanceof HttpServletRequest && servletResponse instanceof HttpServletResponse;
    }

    private String getResBodyString(ContentCachingResponseWrapper responseWrapper) throws IOException {
        IOUtils.toByteArray(responseWrapper.getContentInputStream());
        byte[] httpResponseBody = responseWrapper.getContentAsByteArray();
        String httpResBodyString = new String(httpResponseBody, responseWrapper.getCharacterEncoding());
        responseWrapper.copyBodyToResponse();
        return httpResBodyString;
    }

    private void logInfo(String uri, String method, String type, String params) {
        if (Utils.isNotBlank(params)) {
            params = params.replaceAll("[\\r\\n\\t\\s]+", "");
        }
        log.info("{} {} {} {}", uri, method, type, params);
    }

    private boolean isGetMethod(HttpServletRequest httpRequest) {
        return httpRequest != null && "GET".equalsIgnoreCase(httpRequest.getMethod());
    }

    private boolean isPostMethod(HttpServletRequest httpRequest) {
        return httpRequest != null && "POST".equalsIgnoreCase(httpRequest.getMethod());
    }
}
